from flask import Flask, request
from flask_restplus import Resource, Api, fields

app = Flask(__name__)
api = Api(app)
app.config['ERROR_404_HELP'] = False

"""
最简单的api

curl http://127.0.0.1:5000/hello
{"hello": "world"}

"""


@api.route('/hello')
class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}


@app.route("/hello")
def hello():
    return {'hello': 'world1'}


"""
$ curl http://localhost:5000/todo1 -d "data=Remember the milk" -X PUT
{"todo1": "Remember the milk"}
$ curl http://localhost:5000/todo1
{"todo1": "Remember the milk"}
$ curl http://localhost:5000/todo2 -d "data=Change my brakepads" -X PUT
{"todo2": "Change my brakepads"}
$ curl http://localhost:5000/todo2
{"todo2": "Change my brakepads"}
或者
>>> from requests import put, get
>>> put('http://localhost:5000/todo1', data={'data': 'Remember the milk'}).json()
{u'todo1': u'Remember the milk'}
>>> get('http://localhost:5000/todo1').json()
{u'todo1': u'Remember the milk'}
>>> put('http://localhost:5000/todo2', data={'data': 'Change my brakepads'}).json()
{u'todo2': u'Change my brakepads'}
>>> get('http://localhost:5000/todo2').json()
{u'todo2': u'Change my brakepads'}
"""
todos = {}


@api.route('/<string:todo_id>')
class TodoSimple(Resource):
    def get(self, todo_id):
        return {todo_id: todos[todo_id]}
        #  return {'task': 'Hello world'}, 201
        # return {'task': 'Hello world'}, 201, {'Etag': 'some-opaque-string'}

    def put(self, todo_id):
        todos[todo_id] = request.form['data']
        return {todo_id: todos[todo_id]}


"""
多个路由指向一个视图
"""


# api.add_resource(HelloWorld, '/hello', '/world')
# or
@api.route('/hello1', '/world')
class HelloWorld2(Resource):
    def get(self):
        return {'hello': 'world'}


"""
指定端点
"""


# ndpoint不设置的话默认是对应的视图函数
@api.route('/todo/<int:todo_id>', endpoint='todo_ep')
class Todoss(Resource):
    def get(self, todo_id):
        return {'hello': 'world' + str(todo_id)}


"""
或者使用该模式
"""


class todo_ep(Resource):
    def get(self, todo_id):
        return {'hello': '1234124324' + str(todo_id)}


# api.add_resource(todo_ep, '/todo/<int:todo_id>', endpoint='todo_ep')


"""
其中，parser.add_argument('rate', type=int,required=True,help='Rate to charge for this resource')表示，参数名为rate，数据类型为int，请求时必须发送此参数，如果验证不通过时将会返回help指定的信息。

运行程序并使用curl进行访问，分别验证以下几种情况：

提供rate值，但不是int型（验证不通过）
提供rate值，且是int型（验证通过）
不提供rate值（验证不通过）
curl http://localhost:5000/1/2 -d "data=walk" -d "rate=foo" -X PUT
{"errors": {"rate": "Rate to charge for this resource invalid literal for int() with base 10: 'foo'"}, "message": "Input payload validation failed"}
curl http://localhost:5000/1/2 -d "data=walk" -d "rate=1" -X PUT
{"2": "walk"}
curl http://localhost:5000/1/2 -d "data=walk" -X PUT
{"errors": {"rate": "Rate to charge for this resource Missing required parameter in the JSON body or the post body or the query string"}, "message": "Input payload validation failed"}
另外，以参数strict=True调用parse_args()能够保证如果请求中包含了解析器中未定义的参数时，将会抛出一个错误。示例程序如下
args = parser.parse_args()
todos1[todo_id] = request.form['data']
改为
args = parser.parse_args(strict=True)
todos[todo_id] = todo_id
就不能给其他参数了
"""
from flask_restplus import reqparse

parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, required=True, help='Rate to charge for this resource')

todos1 = {
    '1': 'eat',
    '2': 'sleep'
}


@api.route('/1/<string:todo_id>')
class TodoSimple1(Resource):
    def get(self, todo_id):
        return {todo_id: todos1[todo_id]}

    def put(self, todo_id):
        args = parser.parse_args()
        todos1[todo_id] = request.form['data']
        return {todo_id: todos1[todo_id]}


"""
数据格式化
fields.DateTime(dt_format='rfc822')
"""
model = api.model('Model', {
    'task': fields.String,
    'uri': fields.Url('todo_ep2', absolute=True),  # absolute参数表示生成的url是否是绝对路径
    'developer': fields.String(default='jack')
})


class TodoDao(object):
    def __init__(self, todo_id, task, developer):
        self.todo_id = todo_id
        self.task = task
        self.developer = developer

        # 该字段不会发送到响应结果中
        self.status = 'active'


"""
curl http://localhost:5000/2/todo2
{"task": "Remember the milk", "uri": "http://localhost:5000/2/todo2"}
"""


@api.route('/2/todo2', endpoint='todo_ep2')
class Todo2(Resource):
    # @api.marshal_with(model)
    # def get(self, **kwargs):
    #     return TodoDao(todo_id='my_todo', task='Remember the milk',developer='Tom')

    # or
    def get(self, **kwargs):
        """
        顺序保留
 默认情况下，字段顺序并未得到保留，因为它会损耗性能。不过，如果你确实需要保留字段顺序，那么可以向类或函数传入一个ordered=True的参数项，以此强制进行顺序保留：

Api全局保留：api = Api(ordered = True)
Namespace全局保留：ns = Namespace(ordered=True)
marshal()局部保留：return marshal(data, fields, ordered=True)
        :param kwargs:
        :return:
        """
        return api.marshal(TodoDao(todo_id='my_todo', task='Remember the milk', developer='Tom'), model, ordered=False)


@api.route('/my-resource/<id>', endpoint='my-resource')
@api.doc(params={'id': 'An ID'})
class MyResource(Resource):
    def get(self, id):
        return {}

    @api.doc(responses={403: 'Not Authorized'})
    def post(self, id):
        api.abort(403)

parent = api.model('Parent', {
    'name': fields.String,
    'class': fields.String(discriminator=True)
})

child = api.inherit('Child', parent, {
    'extra': fields.String
})



resource_fields = api.model('Resource', {
    'name': fields.String,
})

@api.route('/1/my-resource/<id>', endpoint='my-resource2')
class MyResource(Resource):
    @api.marshal_with(resource_fields, as_list=True)
    def get(self):
        return get_objects()

    @api.marshal_with(resource_fields, code=201)
    def post(self):
        return create_object(), 201



if __name__ == '__main__':
    app.run(debug=True)
